package object

import (
	"code.byted.org/bytertc/whiteboard_server/biz/api/pb"
	v1 "code.byted.org/bytertc/whiteboard_server/biz/api/pb/v1"
	"code.byted.org/bytertc/whiteboard_server/biz/pkg/biz_error"
	"github.com/pkg/errors"
	"google.golang.org/protobuf/proto"
)

func NewElement(i Info, obj proto.Message) Element {
	switch v := obj.(type) {
	case *v1.Pen:
		return &Pen{ElementInfo{i}, v}
	}
	return &DefaultElement{ElementInfo{i}, obj}
}

func CreateElementFromAction(appId, roomId, pageId string, boardId int32, action *v1.Action, data proto.Message) Element {
	i := Info{
		AppId:  appId,
		RoomId: roomId,
		Element: &v1.Element{
			Type:          action.Type,
			ActionCount:   InitialActionCount,
			ActionId:      action.ActionId,
			Timestamp:     action.Timestamp,
			BoardId:       boardId,
			PageId:        pageId,
			ObjId:         action.ObjId,
			ZIndex:        action.ZIndex,
			CreatorUserId: action.CreatorUserId,
		},
	}
	return NewElement(i, data)
}

func NewElementFromRaw(appId, roomId string, rawElement []byte) (Element, error) {
	eleMsg := &v1.Element{}
	err := proto.Unmarshal(rawElement, eleMsg)
	if err != nil {
		return nil, biz_error.DecodeError.Wrap(errors.Wrap(err, "fail to unmarshal Action"))
	}
	msg, err := pb.UnmarshalAction(eleMsg.Type, eleMsg.Data)
	if err != nil {
		return nil, biz_error.DecodeError.Wrap(errors.Wrapf(err, "fail to unmarshal %s", eleMsg.Type))
	}
	eleMsg.Data = nil
	i := Info{
		AppId:   appId,
		RoomId:  roomId,
		Element: eleMsg,
	}
	return NewElement(i, msg), nil
}

type ElementInfo struct {
	Info
	// 此处应有容器 😅
}

func (e *ElementInfo) AppendPoints(points PenPoints) {}

func (e *ElementInfo) Set(another ProtoObject) {}

func (e *ElementInfo) SetStyle(style Style) {}

func (e *ElementInfo) SetRange(r Range) {}

// DefeaultElement 元素默认使用全量替换
type DefaultElement struct {
	ElementInfo
	ProtoObject
}

func (d *DefaultElement) Set(another ProtoObject) {
	d.ProtoObject = another
}

func (d *DefaultElement) SetStyle(style Style) {
	newStyle := style.GetStyle()
	if newStyle == nil {
		return
	}
	if v, ok := d.ProtoObject.(Style); ok {
		v.GetStyle().Width = newStyle.Width
		v.GetStyle().Color = newStyle.Color
		v.GetStyle().Dash = newStyle.Dash
		v.GetStyle().FillColor = newStyle.FillColor
	}
}

func (d *DefaultElement) SetRange(r Range) {
	if v, ok := d.ProtoObject.(Range); ok {
		setPoint(v.GetStart(), r.GetStart())
		setPoint(v.GetEnd(), r.GetEnd())
	}
}

func setPoint(dest *v1.Point, src *v1.Point) {
	if src == nil {
		return
	}
	dest.X = src.X
	dest.Y = src.Y
}

func (d *DefaultElement) ToProto() *v1.Element {
	return d.toProto(d.ProtoObject)
}

// Pen 画笔包含的点数据为追加
type Pen struct {
	ElementInfo
	*v1.Pen
}

func (p *Pen) Set(another ProtoObject) {
	if v, ok := another.(*v1.Pen); ok {
		p.Pen = v
	}
}

func (p *Pen) AppendPoints(points PenPoints) {
	p.Pen.Points = append(p.Pen.Points, points.GetPoints()...)
}

func (p *Pen) ToProto() *v1.Element {
	return p.toProto(p.Pen)
}
